/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package edu.pdx.cs.guavadata.query;

import edu.pdx.cs.guavadata.Channel;
import edu.pdx.cs.guavadata.Transform;
import edu.pdx.cs.guavadata.domain.Domain;
import edu.pdx.cs.guavadata.function.Function;
import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author VuTran
 */
public class ApplyOp extends Operator {

    // Function application relational operator: input columns, output columns, and the function to apply

    Operator child;
    List<String> input;
    List<String> output;
    Function f;

    public Operator getChild() {
        return child;
    }

    public void setChild(Operator child) {
        this.child = child;
    }

    public Function getF() {
        return f;
    }

    public void setF(Function f) {
        this.f = f;
    }

    public List<String> getInput() {
        return input;
    }

    public void setInput(List<String> input) {
        this.input = input;
    }

    public List<String> getOutput() {
        return output;
    }

    public void setOutput(List<String> output) {
        this.output = output;
    }

    public ApplyOp(Operator child, List<String> input, List<String> output, Function f) {
        this.child = child;
        child.parent = this;
        this.input = new ArrayList<String>(input);
        this.output = new ArrayList<String>(output);
        this.f = f;

        try {
            // Dummy check: input column count = function input count
            if (this.input.size() != f.getInputs().size())
                throw new Exception("Number of function inputs does not match the number of input columns.");

            // Dummy check: output column count = function output count
            if (this.output.size() != f.getOutputs().size())
                throw new Exception("Number of function outputs does not match the number of output columns.");

            // Assign columns, and if input domain does not match, throw an error
            this.exposedColumns = new ArrayList<String>();
            this.exposedDomains = new ArrayList<Domain>();
            for (int i = 0; i < child.exposedColumns.size(); i++) {
                String name = child.exposedColumns.get(i);
                if (this.input.contains(name)) {
                    // check to see if domain match
                    if (!f.getInputs().get(this.input.indexOf(name)).equals(child.exposedDomains.get(i))) {
                        throw new Exception("Domains, " + f.getInputs().get(this.input.indexOf(name)) +
                                ", do not match, " + child.exposedDomains.get(i) + ", for column " + name + ".");
                    }
                } else {
                    // Propagate non-function column domains
                    this.exposedColumns.add(name);
                    this.exposedDomains.add(child.exposedDomains.get(i));
                }
            }

            // Add the output function column domains
            for (int i = 0; i < this.output.size(); i++) {
                this.exposedColumns.add(this.output.get(i));
                this.exposedDomains.add(f.getOutputs().get(i));
            }
        } catch (Exception e) {
            System.err.println("ERROR: " + e.toString());
        }
    }

    @Override
    public SqlText visit(Provider qv) {
        return qv.transform(this);
    }

    @Override
    public void visit(Channel c, String table) {
        c.mapAliases(this, table);
    }

    @Override
    public void visit(Transform t, Command c) {
        t.expand(this, c);
    }

    @Override
    public void notifyChange(Operator old, Operator newOp) {
        if (child == old) {
            child = newOp;
            newOp.parent = this;
        }
    }

    @Override
    public Operator clone() {
        return new ApplyOp(child.clone(), input, output, f);
    }

}
